Repository navigation
fix(runtime,plugin-security): the declared-webhook activation door switches one of two holders a namespace cannot tell apart, and the upgrade report names each withheld holder - #33204
Conversation
…mespace address cannot tell apart, and the upgrade report names each disabled holder Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibxGWY9FF7Lgr4CiTa3
…er switch and the disabled co-holder; docs name the door Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibxGWY9FF7Lgr4CiTa3
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibxGWY9FF7Lgr4CiTa3
📓 Docs Drift CheckThis PR changes 3 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 179 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cd5284edd0ae3a7da95c8ee8bb98e40128b1ce15 && git checkout cd5284edd0ae3a7da95c8ee8bb98e40128b1ce15
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 67274acad689d5be75d5bfc5007f4fb9396aa907 0afa1e4627236ecff76807c9c4e81996d6c7c603 && git checkout -B drift-repro 67274acad689d5be75d5bfc5007f4fb9396aa907 && git merge --no-ff 0afa1e4627236ecff76807c9c4e81996d6c7c603
node scripts/docs-audit/affected-docs.mjs --json 67274acad689d5be75d5bfc5007f4fb9396aa907
|
|
epic:#31527#3 · os-tesla · session_01L9qibzECS2npZFku4956Um Contract reviewServed-tier: Inputs: card #33016 (body, triage audit 6847357630, claim 6868795624, dev report 6870083520), PR #33204 (body, 11 files, net diff against merge-base ① Derived judgmentsAccept-set and public-surface changes the diff implies, each judged:
Findings (none is a must-fix):
② Semver level
③ Boundary flags
Check-runs on
The verdict below is on the contract. The seat waits for every running check to conclude green before it lands anything; a red one voids nothing here but blocks the landing. The PR is still a draft. Implemented-by: VERDICT: PASS |
…ys to enable it first; pin the holder with no namespace The withheld-webhook finding of a disabled package is marked packageDisabled and its line says the package must be enabled before its exact-qualifier address switches it. The report's webhook switch drops the bare-address arm no finding reaches. Pins a package declaring no namespace beside another holder, in both registration orders, at the door and in the dispatcher. The door's module doc names the webhook's query parameter; the changeset names the response's packageId and the spec liveness text. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibxGWY9FF7Lgr4CiTa3
|
epic:#31527#3 · os-tesla · session_01L9qibzECS2npZFku4956Um Contract reviewServed-tier: Delta round: this record replaces 6870153173 (on ① Derived judgmentsAccept-set and public-surface changes the net diff implies, each judged:
Findings of record 6870153173, each re-judged on this head:
New findings (none is a must-fix):
② Semver level
③ Boundary flags
Check-runs on
The verdict below is on the contract. The seat waits for every running check, the Test Core shards included, to conclude green before it lands anything. The PR is still a draft. Implemented-by: VERDICT: PASS |
|
epic:#31527#3 · os-tesla · session_01L9qibzECS2npZFku4956Um 席位核对 · ACCEPT · PR #33204 (#33016 items 1 to 6, 8) · 2026-11-14T03:43ZHead-sha:
Generated by Claude Code |
epic:#31527#3 · os-tesla · session_01L9qibzECS2npZFku4956Um
Requested by Tesla · project thread
Refs #33016 (items 1, 2, 3, 4, 5, 6, 8)
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
POST /security/_activation/webhook/NAME新增?package=PKG精确限定,能单独开关同名的两个持有者之一;升级影响报告对每个持有者(含被禁用的第二个)各报一条,并给出该持有者的开关地址?package=PKG(被禁用的持有者须先启用其包再开关);两条平台测试清单与 liveness 证据改到新的declared:PKG/NAME引用。packages/runtime/src/domains/catalog-activation.ts(resolveExactWebhook与查询集)、packages/plugins/plugin-security/src/upgrade-impact-report.ts(webhookShippers)、packages/runtime/src/domains/catalog-activation-door.test.ts。pnpm --filter @objectstack/runtime exec vitest run src/domains/catalog-activation-door.test.ts,应见两个注册顺序下?package=各写自己的一行、两扇门都 409 点名双方。What changes
Item 1 (S1), the door. Two holders the namespace address cannot tell apart, two co-owners of one namespace or a package that declares no namespace beside another holder, are both served and both dispatched, and both
webhook/NAMEandwebhook/NS/NAMEanswer409naming both, so neither could be switched. The bare address now takes?package=PKG(resolveExactWebhook,WEBHOOK_DOOR_QUERY_PARAMSinpackages/runtime/src/domains/catalog-activation.ts), the exact qualifier/metareads (ADR-0150 D2) and the permission-set door already takes: it addresses exactly PKG's served declaration and writes PKG's own(webhook, package_id, name)row (WEBHOOK_LEDGER_ADDRESSING), the row naming no package left as it is under two holders and taken over under one (D8,webhookAdoptsPackageless). A PKG that does not serve the name, a disabled one included, is409 RESOURCE_CONFLICTnot-held-by-qualifiernaming the served holders, or404when nothing serves it.packageis the one query parameter of the bare webhook address (AGENTS route rule 5, read by the sharedreadPermissionSetPackageQuery); the namespace address takes none.FROM → TO rows (Clause-② widening):
POST /security/_activation/webhook/NAME?package=PKG, PKG serving NAME: FROM400 VALIDATION_ERROR(unknown query parameter) TO200, PKG's row written,data.packageId= PKG.400TO409 RESOURCE_CONFLICTnaming the holders (or404when no enabled package declares NAME). Nothing written.POST /security/_activation/webhook/NS/NAME?package=…:400before and after.Item 2 (S3). Pinned: B disabled beside A enabled, B's namespace address is
409naming A (no code change).Item 3 (S5).
integration-system.webhook-lifecycle(steps[1]) andcli.organization-ownership-ceremony(acceptance[5].verify) expectref_id declared:com.example.showcase/showcase_task_changed, aspackages/qa/dogfood/test/upgrade-18x-harness.dogfood.test.tspins; each item's revision and history bumped.Item 4 (nit 1).
packages/spec/liveness/webhook.jsonnameevidence: one slot per served(package, name)holder anddeclaredWebhookRefId(name, packageId).Item 5 (nit 2). The upgrade-impact report's switch line (
SWITCH_DOORS) and withheld-webhook line namewebhook/NAME?package=PACKAGE, where they named the namespace address. It addresses the finding's package whatever its namespace, once that package is enabled: a disabled package holds nothing for the door, so its address answers409until it is enabled again (item 6). Every webhook finding names a package (a re-enablement's row records one, a withheld webhook's holder is a package), so the line carries no bare-address arm.Item 6 (nit 3).
webhookShippersalso counts each holderpackageScopedHoldersOf(…, { includeDisabledPackages: true })answers, confirmed against its own code artifact (shipsAt), so a disabled second holder gets its withheld finding. FROM one finding (the holder the bare artifact lookup answered) TO one finding per holder. A disabled holder's finding is markedpackageDisabled: true(an added optional field), and the withheld-webhook line says such a package must be enabled first, then switched at?package=PACKAGE, so the line never sends the operator to a door that answers409.Item 7. Not in this PR: a security item is with the owner seat.
Item 8 (nit 4). Pinned: one holder with its own row beside a row naming no package, the bare address writes the holder's own row (no code change).
Responsibility. Producing code: the runtime activation door (
handleCatalogActivationWrite) and plugin-security's boot report (collectUpgradeImpactFindings). Existing platform path: none switched one of two holders a namespace cannot tell apart; a package disable or a hand-written ledger row were the only stops. Reached by: an operator (manage_metadata, the platform operator under a wall) whose deployment holds two such webhook holders, rare; the report and checklist lines by every boot and every checklist run.Rulings quoted. Epic ruling 6842178655 on PR #32577 ("Follow-up card (p2), not this PR") files these items; ADR-0150 D2 (
?package=is the exact qualifier), D4 (one row per holder), D5 (qualified addresses), D8 (a row naming no package is the one holder's). Surface grepruling|Ruling-ref|maintainer|⛔hits:catalog-activation.ts8 (ADR-0131 seeder ruling, #22621 → A, ADR-0150 D2 ruling, epic ruling MF1 ×3, four ⛔ door limits),upgrade-impact-report.ts1 (the ruling on #24443),webhook.json3 (the #7131 previews ruling, the spec-only override note); none is touched by this diff.File-surface breaches (seat extended the surface):
packages/runtime/src/route-ledger.ts(one sentence on thewebhook/:ns/:namerow naming?package=) andcontent/docs/automation/webhooks.mdx(one sentence naming the exact qualifier).Red lines
409naming both, in both registration orders (catalog-activation-door.test.ts›[FROM 409 TO switched] … registered alpha, betaand… registered beta, alpha);?package=resolves only an exact served holder and refuses every other PKG naming the holders (… a package that does not hold the name is 409 …,… a disabled package holds nothing …); the bare read of two co-owners still throws the 409 naming both (declared-webhooks.test.ts›two co-owners of one namespace, registered alpha, beta/beta, alpha).?package=write never takes over the row naming no package ([null, false]kept beside the holder's own row in both orders); the report names each holder rather than the first one found (upgrade-impact-report.test.ts›[FROM one TO each] registered com.acme.crm, com.acme.addon …and the reversed order).CONTROL — one holder: the bare address answers as before …,CONTROL — one holder: its namespace address writes the same row,CONTROL — one holder: \?package=` writes the row the bare address writes …,[FROM 409 TO written] one holder with its own row beside a row naming no package …`); bare-name URL behaviour is unchanged (every pre-existing door test unmodified and green); no migration and no ADR-0087 entry: no stored shape or authorable key moves.Tests
catalog-activation-door.test.ts10 cases (S3, the?package=describe ×8: two co-owners of one namespace and a package with no namespace beside a holder, each in both orders, nit 4);declared-webhooks.test.ts4 (two co-owners, and a package with no namespace beside a namespaced holder, each in both orders: both dispatched under their own ids, the bare read refused naming both);upgrade-impact-report.test.ts2 (disabled second holder, both orders, its finding markedpackageDisabledand the line saying to enable first) + 1 rewritten (the remedy names the exact-qualifier address and no bare-address arm).scripts/ablation-replace.mjs, test imports resolve tosrc/, restored blob == HEAD):registered alpha, beta,registered beta, alpha,a package with no namespace beside a holder×2,a package that does not hold the name …,a disabled package holds nothing …,CONTROL — one holder: ?package= …);webhookShippers⇒ 2 red (both registration orders);… the remedy names the exact-qualifier address);packageDisabledmark removed, or the enable-first sentence removed from the line ⇒ 2 red each (both registration orders).Acceptance notes
plugin-webhooks' own remedies (the name guard and the redelivery veto,declaredWebhookActivationPath) still name the slot's namespace address, as ruled for objectql: narrow the phase-1 one-holder refusal from environment to namespace for webhook, connector and email_template (ADR-0150 child B, webhook family) #31699 round 2; for two co-owners of one namespace that address answers409, and?package=is the one that switches. Not changed here (a ruled text); the seat files the follow-up.409naming the environment and the package,?package=PKGand the namespace address answer404, nothing is written. No holder has a working switch there, and nothing is delivered either. Left to the seat.Verification
All readings at head
0afa1e462(the review's patch round onc68c098f0; branch base4c152685,origin/mainf45c0125merged once; fullpnpm buildgreen atc68c098f0, 119/119 tasks).Tests and typecheck
pnpm --filter @objectstack/runtime typecheck && test: Test Files 597 passed (597), Tests 10619 passed, 19 skipped; lock VERDICT command-exit 0.pnpm --filter @objectstack/plugin-security test: Test Files 267 passed, Tests 6607 passed (45 skipped);pnpm --filter @objectstack/plugin-webhooks test: Test Files 25 passed, Tests 296 passed.typecheckof@objectstack/runtime,@objectstack/plugin-security,@objectstack/plugin-webhooks: exit 0.Gates
node scripts/pm/dispatch-gates.mjs --commandsderived 105 families (the same 105 as atc68c098f0); all 105 run with stdin closed, every exit code 0;--ranreconciliation: 105 derived, 105 run, 0 NOT-MEASURED, 0 UNRUN.pnpm lintexit 0;pnpm --filter @objectstack/spec check:generatedexit 0.c68c098f0the first run (before the merge) found two real problems, fixed in95e17a456:check:platform-checklistwanted each item's top-levelrevisionbumped with its history entry. Its three exit-3 prerequisite refusals (check:skill-examples,check:dual-build-cjs-loads, the teardown-shape self-test's fixture commit) andcheck:dts-closurewere local build-state, cleared by the full build and the fixture fetch; all four exit 0 on the final run.Ablations
Each via
node scripts/ablation-replace.mjs(anchor 1 → 0 on disk, restored blob == HEAD,git diff HEADempty); the suites importsrc/directly.catalogActivationQueryParams: the webhook bare address'spackagetaken away ⇒catalog-activation-door.test.ts7 failed / 84 passed.webhookShippers: the disabled-holder loop's add removed ⇒upgrade-impact-report.test.ts2 failed / 43 passed (atc68c098f0; the loop is unchanged since).WEBHOOK_SWITCH_DOOR: the namespace address put back ⇒ 1 failed / 44 passed.packageDisabledspread deleted ⇒upgrade-impact-report.test.ts2 failed / 43 passed.dogfood_battery: not run (no
!in the title and no(narrowing)arm).Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L9qibxGWY9FF7Lgr4CiTa3